// Effective C++ 条款44：将与参数无关的代码抽离模板
// 核心概念
// 条款44讨论了模板代码膨胀(code bloat)问题，以及如何通过将与模板参数无关的代码抽离出来，减少代码重复和二进制文件大小。

// 模板代码膨胀问题
// 当使用模板时，编译器会为每个不同的模板参数实例化一个完整的模板副本。这可能导致代码膨胀：相同的代码在多个实例中重复出现。

template <typename T>
void printElements(const std::vector<T> &vec)
{
    // 这段代码对所有T类型都是相同的
    std::cout << "Vector contains " << vec.size() << " elements:" << std::endl;

    // 这段代码依赖于T类型
    for (const auto &elem : vec)
    {
        std::cout << elem << std::endl;
    }
}

// 使用不同类型实例化
std::vector<int> intVec = {1, 2, 3};
std::vector<double> doubleVec = {1.1, 2.2, 3.3};
std::vector<std::string> stringVec = {"one", "two", "three"};

printElements(intVec);    // 生成一个实例
printElements(doubleVec); // 生成另一个实例
printElements(stringVec); // 生成第三个实例

// 在这个例子中，输出向量大小的代码在每个实例中都是重复的，尽管它与模板参数T无关。

// 解决方案：代码抽离
// 1. 共性与变性分析
// 第一步是识别模板中哪些部分依赖于模板参数（变性），哪些部分不依赖（共性）：
//      共性代码：与模板参数无关，可以抽离
//      变性代码：依赖于模板参数，必须保留在模板中

// 2. 三种主要的代码抽离技术
// 技术一：非类型模板参数
// 将共性代码移至使用非类型模板参数的基类：
// 共性基类
template <typename T, std::size_t n>
class ArrayBase
{
protected:
    // 与T无关的代码
    void inBounds(std::size_t index) const
    {
        if (index >= n)
            throw std::out_of_range("Array index out of bounds");
    }
};

// 派生模板类
template <typename T, std::size_t n>
class Array : private ArrayBase<T, n>
{
private:
    T data[n];

public:
    T &operator[](std::size_t index)
    {
        this->inBounds(index); // 使用基类的共性代码
        return data[index];    // 处理T相关的操作
    }
};

// 技术二：类型参数替换为函数参数
// 将共性代码移至非模板基类，使用函数参数而非模板参数：
// 非模板基类
class LockBase
{
protected:
    void lock() { /* 通用锁定代码 */ }
    void unlock() { /* 通用解锁代码 */ }
};

// 模板派生类
template <typename T>
class LockGuard : private LockBase
{
private:
    T &resource;

public:
    explicit LockGuard(T &res) : resource(res)
    {
        lock(); // 使用基类的共性代码
    }

    ~LockGuard()
    {
        unlock(); // 使用基类的共性代码
    }
};

// 技术三：将共性代码放入非模板函数
// 最直接的方法是将共性代码提取到普通函数中：
// 非模板辅助函数
void printHeader(std::size_t size)
{
    std::cout << "Vector contains " << size << " elements:" << std::endl;
}

// 模板函数只保留与T相关的代码
template <typename T>
void printElements(const std::vector<T> &vec)
{
    printHeader(vec.size()); // 调用非模板函数处理共性代码

    // 只保留依赖于T的代码
    for (const auto &elem : vec)
    {
        std::cout << elem << std::endl;
    }
}

// 实际案例分析
// OpenCV Mat类的设计思路
// OpenCV的Mat类采用了更复杂但高效的设计：
// 1.类型无关的基类：MatBase/MatAllocator处理内存分配、引用计数等共性操作
// 2.数据类型参数化：通过模板参数和运行时类型信息处理不同数据类型
// 4.算法分离：将矩阵操作算法与数据存储分离

// 非模板基类 - 处理所有与类型无关的操作
class MatrixBase
{
protected:
    void *data;
    size_t rows, cols;
    int refCount;

    // 共性代码：内存管理
    void allocateStorage(size_t r, size_t c, size_t elemSize)
    {
        rows = r;
        cols = c;
        if (r > 0 && c > 0)
        {
            data = malloc(r * c * elemSize);
            refCount = 1;
        }
        else
        {
            data = nullptr;
            refCount = 0;
        }
    }

    void releaseStorage()
    {
        if (data && --refCount == 0)
        {
            free(data);
            data = nullptr;
        }
    }

    void addReference()
    {
        if (data)
            ++refCount;
    }

    // 共性代码：检查边界
    void checkBounds(size_t r, size_t c) const
    {
        if (r >= rows || c >= cols)
            throw std::out_of_range("Matrix index out of bounds");
    }

    // 共性代码：检查维度匹配
    bool checkDimensions(const MatrixBase &other) const
    {
        return rows == other.rows && cols == other.cols;
    }

    // 共性代码：转置索引计算
    size_t transposeIndex(size_t r, size_t c) const
    {
        return c * rows + r; // 转置后的索引计算
    }
};

// 模板类 - 只处理与类型相关的操作
template <typename T>
class Matrix : private MatrixBase
{
public:
    Matrix(size_t r, size_t c)
    {
        allocateStorage(r, c, sizeof(T));
        // 初始化元素 - 类型相关
        T *typedData = static_cast<T *>(data);
        for (size_t i = 0; i < r * c; ++i)
        {
            typedData[i] = T(); // 使用T的默认构造函数
        }
    }

    ~Matrix()
    {
        releaseStorage();
    }

    // 拷贝构造函数 - 分离共性和变性代码
    Matrix(const Matrix &other)
    {
        allocateStorage(other.rows, other.cols, sizeof(T));
        // 复制元素 - 类型相关
        if (data)
        {
            std::memcpy(data, other.data, rows * cols * sizeof(T));
        }
    }

    // 赋值操作符 - 分离共性和变性代码
    Matrix &operator=(const Matrix &other)
    {
        if (this != &other)
        {
            releaseStorage();
            allocateStorage(other.rows, other.cols, sizeof(T));
            // 复制元素 - 类型相关
            if (data)
            {
                std::memcpy(data, other.data, rows * cols * sizeof(T));
            }
        }
        return *this;
    }

    // 元素访问 - 类型相关
    T &operator()(size_t r, size_t c)
    {
        checkBounds(r, c); // 使用基类的边界检查
        return static_cast<T *>(data)[r * cols + c];
    }

    const T &operator()(size_t r, size_t c) const
    {
        checkBounds(r, c); // 使用基类的边界检查
        return static_cast<T *>(data)[r * cols + c];
    }

    // 转置操作 - 分离共性和变性代码
    Matrix transpose() const
    {
        Matrix result(cols, rows); // 行列互换

        if (data && result.data)
        { // 使用基类的空检查逻辑
            T *srcData = static_cast<T *>(data);
            T *dstData = static_cast<T *>(result.data);

            // 转置操作 - 类型相关部分
            for (size_t i = 0; i < rows; ++i)
            {
                for (size_t j = 0; j < cols; ++j)
                {
                    // 使用基类的转置索引计算
                    size_t srcIdx = i * cols + j;
                    size_t dstIdx = transposeIndex(i, j);
                    dstData[dstIdx] = srcData[srcIdx];
                }
            }
        }

        return result;
    }

    // 矩阵加法 - 分离共性和变性代码
    Matrix operator+(const Matrix &other) const
    {
        if (!checkDimensions(other))
        { // 使用基类的维度检查
            throw std::invalid_argument("Matrix dimensions don't match");
        }

        Matrix result(rows, cols);

        if (data && other.data && result.data)
        {
            T *srcData1 = static_cast<T *>(data);
            T *srcData2 = static_cast<T *>(other.data);
            T *dstData = static_cast<T *>(result.data);

            // 加法操作 - 类型相关部分
            for (size_t i = 0; i < rows * cols; ++i)
            {
                dstData[i] = srcData1[i] + srcData2[i]; // 使用T的+操作符
            }
        }

        return result;
    }

    // 其他操作...
};

// 1.将与模板参数T无关的代码放入非模板基类
//      所有不依赖于T的操作（边界检查、内存管理、维度验证等）
//      这部分代码在二进制文件中只存在一份
// 2.只在模板派生类中保留与T相关的代码
//      元素类型的操作（构造、复制、算术运算等）
//      这部分代码确实需要为每种T类型生成不同的实例

// 这种设计模式的好处是：
// -减少代码膨胀：避免相同代码在多个模板实例中重复
// -提高编译速度：减少需要编译的代码量
// -减小二进制文件大小：共性代码只存储一次
// -提高代码可维护性：逻辑更清晰，职责分明
// 这是C++模板编程中一个非常实用的技巧，特别是在开发大型库或框架时，能显著提高效率和可维护性。